package com.yxy.service.impl;

import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.yxy.common.PageResult;
import com.yxy.constant.MessageConstant;
import com.yxy.constant.StatusConstant;
import com.yxy.exception.InsufficientStockException;
import com.yxy.exception.UseWarningException;
import com.yxy.mapper.HotelBookingMapper;
import com.yxy.mapper.HotelMapper;
import com.yxy.mapper.UserMapper;
import com.yxy.pojo.dto.HotelBookingDTO;
import com.yxy.pojo.dto.HotelBookingPageQueryDTO;
import com.yxy.pojo.entity.*;
import com.yxy.pojo.vo.HotelBookingPageQueryVO;
import com.yxy.pojo.vo.HotelBookingVo;
import com.yxy.service.HotelBookingService;
import com.yxy.utils.ExcelUtil;
import lombok.SneakyThrows;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.List;

@Service
public class HotelBookingServiceImpl implements HotelBookingService {

    @Autowired
    private HotelBookingMapper hotelBookingMapper;
    @Autowired
    private HotelMapper hotelMapper;
    @Autowired
    private UserMapper userMapper;

    /**
     * 新增
     *
     * @param hotelBookingDTO
     */
    @Override
    public void add(HotelBookingDTO hotelBookingDTO) {
        // 根据酒店规格id查询规格，计算价格
        HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(hotelBookingDTO.getHotelSpecsId());
        // 判断库存
        int stock = hotelSpecs.getStock() - hotelBookingDTO.getNumber();
        if (stock < 0) {
            throw new InsufficientStockException(MessageConstant.INSUFFICIENT_STOCK);
        }
        // 一天的所有人价格
        BigDecimal oneDayPrice = hotelSpecs.getPrice().multiply(new BigDecimal(hotelBookingDTO.getNumber()));
        // 相差天数（最低一天）
        int days = (int) ChronoUnit.DAYS.between(hotelBookingDTO.getBookingStartTime(), hotelBookingDTO.getBookingEndTime()) + 1;
        // 总价格
        BigDecimal totalPrice = oneDayPrice.multiply(new BigDecimal(days));
        HotelBooking hotelBooking = new HotelBooking();
        BeanUtils.copyProperties(hotelBookingDTO, hotelBooking);
        hotelBooking.setPrice(totalPrice);
        hotelBooking.setStatus(StatusConstant.BOOKING_OK);

        LocalDateTime startTime = hotelBookingDTO.getBookingStartTime();
        LocalDateTime endTime = hotelBookingDTO.getBookingEndTime();
        // 将LocalDateTime转换为UTC的ZonedDateTime
        ZonedDateTime startUtc = startTime.atZone(ZoneId.of("UTC"));
        ZonedDateTime endUtc = endTime.atZone(ZoneId.of("UTC"));
        // 转化内指定时区
        ZonedDateTime startZonedDateTime = startUtc.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        ZonedDateTime endZonedDateTime = endUtc.withZoneSameInstant(ZoneId.of("Asia/Shanghai"));
        // 转换为LocalDateTime（去掉时区信息）
        startTime = startZonedDateTime.toLocalDateTime();
        endTime = endZonedDateTime.toLocalDateTime();
        hotelBooking.setBookingStartTime(startTime);
        hotelBooking.setBookingEndTime(endTime);
        hotelBookingMapper.add(hotelBooking);

        // 减少库存
        hotelMapper.updateSpecs(
                HotelSpecs.builder()
                        .id(hotelBookingDTO.getHotelSpecsId())
                        .hotelId(hotelBookingDTO.getHotelId())
                        .stock(stock)
                        .build());
    }

    /**
     * 分页查询
     *
     * @param hotelBookingPageQueryDTO
     * @return
     */
    @Override
    public PageResult page(HotelBookingPageQueryDTO hotelBookingPageQueryDTO) {
        PageHelper.startPage(hotelBookingPageQueryDTO.getPage(), hotelBookingPageQueryDTO.getPageSize());
        Page<HotelBookingPageQueryVO> page = hotelBookingMapper.pageQuery(hotelBookingPageQueryDTO);
        page.getResult().forEach(item -> {
            // 查询酒店信息
            Hotel hotel = hotelMapper.selectById(item.getHotelId());
            item.setHotelName(hotel.getName());
            item.setCover(hotel.getCover());
            // 获取酒店规格信息
            HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(item.getHotelSpecsId());
            item.setHotelSpecsName(hotelSpecs.getName());
            item.setRoomPrice(hotelSpecs.getPrice());
            // 判断是否过期
            if (item.getStatus() == StatusConstant.BOOKING_OK && item.getBookingEndTime().isBefore(LocalDateTime.now())) {
                // 过期
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .status(StatusConstant.BOOKING_EXPIRED)
                        .build());
                item.setStatus(StatusConstant.BOOKING_EXPIRED);
                // 过期的房间数复位加一
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
            // 判断是否使用并且已超过预约时间
            if (item.getStatus() == StatusConstant.BOOKING_USER && item.getBookingEndTime().isBefore(LocalDateTime.now()) && item.getIsUse() != StatusConstant.HOTEL_USE) {
                // 酒店是否已使用并超过时间
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .isUse(StatusConstant.HOTEL_USE)
                        .build());
                // 过期的房间数复位加一
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
        });
        return new PageResult(page.getTotal(), page.getResult());
    }

    /**
     * 分页查询用户酒店预约
     *
     * @param hotelBookingPageQueryDTO
     * @return
     */
    @Override
    public PageResult pageQueryHotelBooking(HotelBookingPageQueryDTO hotelBookingPageQueryDTO) {
        PageHelper.startPage(hotelBookingPageQueryDTO.getPage(), hotelBookingPageQueryDTO.getPageSize());
        Page<HotelBookingPageQueryVO> page = hotelBookingMapper.pageQueryHotelBooking(hotelBookingPageQueryDTO);
        page.getResult().forEach(item -> {
            // 查询酒店信息
            Hotel hotel = hotelMapper.selectById(item.getHotelId());
            item.setHotelName(hotel.getName());
            // 判断是否过期
            if (item.getStatus() == StatusConstant.BOOKING_OK && item.getBookingEndTime().isBefore(LocalDateTime.now())) {
                // 过期
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .status(StatusConstant.BOOKING_EXPIRED)
                        .build());
                item.setStatus(StatusConstant.BOOKING_EXPIRED);
                // 过期的房间数复位加一
                HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(item.getHotelSpecsId());
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
            // 判断是否使用并且已超过预约时间
            if (item.getStatus() == StatusConstant.BOOKING_USER && item.getBookingEndTime().isBefore(LocalDateTime.now()) && item.getIsUse() != StatusConstant.HOTEL_USE) {
                // 酒店是否已使用并超过时间
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .isUse(StatusConstant.HOTEL_USE)
                        .build());
                // 过期的房间数复位加一
                HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(item.getHotelSpecsId());
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
        });
        return new PageResult(page.getTotal(), page.getResult());
    }

    /**
     * 根据id查询
     *
     * @param id
     * @return
     */
    @Override
    public HotelBookingVo getHotelBookingById(Integer id) {
        HotelBookingVo vo = new HotelBookingVo();
        HotelBooking hotelBooking = hotelBookingMapper.selectById(id);
        BeanUtils.copyProperties(hotelBooking, vo);
        // 预约人信息
        User user = userMapper.selectById(hotelBooking.getUserId());
        vo.setName(user.getName());
        // 酒店信息
        Hotel hotel = hotelMapper.selectById(hotelBooking.getHotelId());
        vo.setHotelName(hotel.getName());
        vo.setCover(hotel.getCover());
        // 酒店规格信息
        HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(hotelBooking.getHotelSpecsId());
        vo.setHotelSpecsName(hotelSpecs.getName());
        vo.setRoomPrice(hotelSpecs.getPrice());
        return vo;
    }

    /**
     * 取消预约
     *
     * @param hotelId
     * @param userId
     * @param bookingId
     */
    @Transactional
    @Override
    public void cancelHotelBooking(Integer hotelId, Integer hotelSpecsId, Integer userId, Integer bookingId) {
        // 修改酒店预约表中的状态
        HotelBooking hotelBooking = HotelBooking.builder()
                .id(bookingId)
                .userId(userId)
                .hotelId(hotelId)
                .hotelSpecsId(hotelSpecsId)
                .status(StatusConstant.BOOKING_CANCEL)
                .build();
        hotelBookingMapper.updateStatus(hotelBooking);
        // 取消预约后回退房间的数量
        HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(hotelSpecsId);
        hotelMapper.updateSpecs(HotelSpecs.builder()
                .id(hotelSpecsId)
                .hotelId(hotelId)
                .stock(hotelSpecs.getStock() + 1)
                .build());
    }

    /**
     * 使用
     *
     * @param id
     * @return
     */
    @Transactional
    @Override
    public String use(int id) {
        // 判断状态
        HotelBooking hotelBooking = hotelBookingMapper.selectById(id);
        // 状态错误
        if (hotelBooking.getStatus() != StatusConstant.BOOKING_OK && hotelBooking.getStatus() != StatusConstant.BOOKING_USER) {
            return StatusConstant.BOOKING_USE_ERROR;
        }
        // 预约已使用
        if (hotelBooking.getStatus() == StatusConstant.BOOKING_USER) {
            return StatusConstant.BOOKING_USE_WARNING;
        }

        hotelBookingMapper.update(SightsBooking.builder()
                .id(id)
                .useTime(LocalDate.now())
                .status(StatusConstant.BOOKING_USER)
                .build());
        // 取消预约后回退房间的数量
        HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(hotelBooking.getHotelSpecsId());
        hotelMapper.updateSpecs(HotelSpecs.builder()
                .id(hotelBooking.getHotelSpecsId())
                .hotelId(hotelBooking.getHotelId())
                .stock(hotelSpecs.getStock() + 1)
                .build());
        return StatusConstant.BOOKING_USE_OK;
    }

    /**
     * 导出
     * @return
     */
    @SneakyThrows
    @Override
    public String exportToExcel() {
        List<HotelBookingPageQueryVO> list = hotelBookingMapper.selectList();
        list.forEach(item -> {
            // 查询酒店信息
            Hotel hotel = hotelMapper.selectById(item.getHotelId());
            item.setHotelName(hotel.getName());
            item.setCover(hotel.getCover());
            // 获取酒店规格信息
            HotelSpecs hotelSpecs = hotelMapper.selectSpecsByHotelSpecsId(item.getHotelSpecsId());
            item.setHotelSpecsName(hotelSpecs.getName());
            item.setRoomPrice(hotelSpecs.getPrice());
            // 判断是否过期
            if (item.getStatus() == StatusConstant.BOOKING_OK && item.getBookingEndTime().isBefore(LocalDateTime.now())) {
                // 过期
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .status(StatusConstant.BOOKING_EXPIRED)
                        .build());
                item.setStatus(StatusConstant.BOOKING_EXPIRED);
                // 过期的房间数复位加一
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
            // 判断是否使用并且已超过预约时间
            if (item.getStatus() == StatusConstant.BOOKING_USER && item.getBookingEndTime().isBefore(LocalDateTime.now()) && item.getIsUse() != StatusConstant.HOTEL_USE) {
                // 酒店是否已使用并超过时间
                hotelBookingMapper.updateStatus(HotelBooking.builder()
                        .id(item.getId())
                        .hotelId(item.getHotelId())
                        .userId(item.getUserId())
                        .hotelSpecsId(item.getHotelSpecsId())
                        .isUse(StatusConstant.HOTEL_USE)
                        .build());
                // 过期的房间数复位加一
                hotelMapper.updateSpecs(HotelSpecs.builder()
                        .id(item.getHotelSpecsId())
                        .hotelId(item.getHotelId())
                        .stock(hotelSpecs.getStock() + 1)
                        .build());
            }
        });
        return ExcelUtil.exportToExcel(list, ExcelUtil.HOTEL);
    }
}
